package com.reuters.rfa.example.omm.domainServer.symbollist;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

import com.reuters.rfa.common.Token;
import com.reuters.rfa.example.framework.prov.ProvDomainMgr;
import com.reuters.rfa.example.omm.domainServer.DataGenerator;
import com.reuters.rfa.example.omm.domainServer.DataStreamItem;
import com.reuters.rfa.example.utility.CommandLine;
import com.reuters.rfa.omm.OMMMapEntry;
import com.reuters.rfa.omm.OMMMsg;

/**
 * SymbolListGenerator provides a simple algorithm to generate list of symbols
 * for Symbol List domain. It can be used to create SymbolListStreamItems.
 * 
 * <p>
 * <b>How to generate response data.</b>
 * </p>
 * 
 * Unique symbols (key) are generated with five random characters. The value of
 * original provider symbol name (PROV_SYMB) of each MapEntry is as same as the
 * symbol key of that entry. The permission(PROD_PERM) are generated by using
 * spcified value. The action is generated randomly (update, add, or delete).
 * 
 * @see SymbolListStreamItem
 */
public class SymbolListGenerator implements DataGenerator
{

    private List<OrderEntry> _orderEntries;
    private List<OrderEntry> _modifiedEntries;

    // the maximum number of caches
    private int _maxEntries = 50;
    private Random _random;

    private int _deleteFactor = 10; // 10% chance of deleting a current entry
    // private int _addFactor = 10; // 10% chance of adding a current entry
    private int _updateFactor = 80; // 80% chance of updating a current entry

    /**
     * Represents a single order entry
     */
    public static class OrderEntry implements Cloneable
    {

        public byte action; // UPDATE, ADD, DELETE
        public String provSymb;
        public int prodPerm;

        public Object clone()
        {
            OrderEntry newEntry = new OrderEntry();
            newEntry.action = action;
            newEntry.provSymb = provSymb;
            newEntry.prodPerm = prodPerm;
            return newEntry;
        }
    }

    public SymbolListGenerator()
    {
        _orderEntries = new ArrayList<OrderEntry>(50);
        _modifiedEntries = new ArrayList<OrderEntry>(50);
        _random = new Random();

        int totalSymbols = CommandLine.intVariable("SYMBOL_LIST_totalSymbols");
        for (int i = 0; i < totalSymbols; i++)
        {
            _orderEntries.add(getNewOrder());
        }
    }

    public DataStreamItem createStreamItem(ProvDomainMgr mgr, Token token, OMMMsg msg)
    {
        boolean encodeDataDef = CommandLine.booleanVariable("SYMBOL_LIST_encodeDataDef");

        SymbolListStreamItem streamItem = new SymbolListStreamItem(this, mgr, token, msg);
        streamItem.setEncodeDataDef(encodeDataDef);
        return streamItem;
    }

    public Object[] getInitialEntries()
    {
        return _orderEntries.toArray();
    }

    public Object[] getNextEntries()
    {
        return _modifiedEntries.toArray();
    }

    public void generateUpdatedEntries()
    {

        _modifiedEntries.clear();
        HashSet<String> newEntries = new HashSet<String>();

        for (int i = 0; i < 5; i++)
        {
            OrderEntry orderEntry = null;
            int action = _random.nextInt(100);

            if (_orderEntries.size() == 0 || action >= _deleteFactor + _updateFactor)
            {
                // Action Add
                if (_orderEntries.size() >= _maxEntries)
                {
                    continue;
                }
                orderEntry = getNewOrder();
                _orderEntries.add(orderEntry);

            }
            else if (action < _deleteFactor)
            {
                // Action Delete
                int index = _random.nextInt(_orderEntries.size());
                orderEntry = (OrderEntry)_orderEntries.get(index);

                if (newEntries.contains(orderEntry.provSymb))
                {
                    continue;
                }
                orderEntry.action = OMMMapEntry.Action.DELETE;
                _orderEntries.remove(orderEntry);

            }
            else
            {
                // Action Update
                int index = _random.nextInt(_orderEntries.size());
                OrderEntry updateEntry = (OrderEntry)_orderEntries.get(index);

                if (newEntries.contains(updateEntry.provSymb))
                {
                    continue;
                }
                updateOrder(updateEntry);
                orderEntry = (OrderEntry)updateEntry.clone();
                orderEntry.action = OMMMapEntry.Action.UPDATE;
            }

            newEntries.add(orderEntry.provSymb);
            _modifiedEntries.add(orderEntry);
        }
    }

    private OrderEntry getNewOrder()
    {

        OrderEntry orderEntry = new OrderEntry();
        String tempSymbol = String.valueOf(new char[] { (char)(_random.nextInt(26) + 65),
                (char)(_random.nextInt(26) + 65), (char)(_random.nextInt(26) + 65), '.',
                (char)(_random.nextInt(26) + 65) });
        try
        {
            orderEntry.provSymb = tempSymbol;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            orderEntry.provSymb = "";
        }
        orderEntry.prodPerm = 3056; // (_random.nextInt(65535)+1);
        orderEntry.action = OMMMapEntry.Action.ADD;

        return orderEntry;
    }

    private void updateOrder(OrderEntry orderEntry)
    {
        // Change only MAP Entry Action.
    }
}
